﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using System.Data;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Reflection;

using LeanEngine.Entity;
using LeanEngine.Collections;

namespace LeanEngine.Data
{
    /// <summary>
    /// Represents an SQL statement or stored procedure to execute against a data source.
    /// </summary>
    public sealed class DataCommand : IDisposable
    {
        #region PropertyInfo Members

        private IDataCommand Command
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the list of LeanEngine.Data.Common.DataParameter class.
        /// </summary>
        public DbParameterCollection Parameters
        {
            get { return this.Command.Parameters; }
        }

        /// <summary>
        /// Gets or sets the text command to run against the data source.
        /// </summary>
        public string CommandText
        {
            get { return this.Command.CommandText; }
            set { this.Command.CommandText = value; }
        }

        /// <summary>
        /// Indicates or specifies how the LeanEngine.Data.Common.DataCommand.CommandText 
        /// property is interpreted.
        /// </summary>
        public CommandType CommandType
        {
            get { return this.Command.CommandType; }
            set { this.Command.CommandType = value; }
        }

        /// <summary>
        /// Gets or sets the wait time before terminating the attempt to execute a command
        /// and generating an error.
        /// </summary>
        public int CommandTimeout
        {
            get { return this.Command.CommandTimeout; }
            set { this.Command.CommandTimeout = value; }
        }

        #endregion

        #region Constructor Members

        /// <summary>
        /// Initialize a new instance of the LeanEngine.Data.Common.DataCommand class
        /// </summary>
        public DataCommand()
        {
            this.Command = DataProvider.NewDataCommand();
            this.CommandTimeout = 0;
        }

        #endregion

        #region Method Members

        /// <summary>
        /// Creates a new instance of a System.Data.Common.DbParameter object.
        /// </summary>
        /// <param name="name">The name of the System.Data.Common.DbParameter.</param>
        /// <param name="value">The value of the parameter.</param>
        public DbParameter NewParameter(string name, object value)
        {
            return this.Command.NewParameter(name, value);
        }

        /// <summary>
        /// Prepare the bulk insert and execute it against the database.
        /// </summary>
        /// <typeparam name="T">The type of the collection elements.</typeparam>
        /// <param name="elementsList">The collection of System.Collections.Generic.IList of type <typeparamref name="T"/>.</param>
        public void ExecuteBulkInsert<T>(IList<T> elementsList)
            where T : PlainEntity
        {
            this.Command.AcquireConnection();

            try
            {
                this.Command.ExecuteBulkInsert(elementsList);
            }
            finally
            {
                this.Command.ReleaseConnection();
            }
        }

        /// <summary>
        /// Prepare the bulk insert and execute it against the database.
        /// </summary>
        /// <typeparam name="T">The type of the collection elements.</typeparam>
        /// <param name="elementsList">The collection of System.Collections.Generic.IList of type <typeparamref name="T"/>.</param>
        /// <param name="timeOut">The time in seconds to wait for the command to execute. The default is 0 seconds.</param>
        public void ExecuteBulkInsert<T>(IList<T> elementsList, int timeOut)
            where T : PlainEntity
        {
            this.Command.AcquireConnection();

            try
            {
                this.Command.ExecuteBulkInsert(elementsList, timeOut);
            }
            finally
            {
                this.Command.ReleaseConnection();
            }
        }

        /// <summary>
        /// Execute a SQL statement against a connection object.
        /// </summary>
        public int ExecuteNonQuery()
        {
            this.Command.AcquireConnection();

            try
            {
                return this.Command.ExecuteNonQuery();
            }
            finally
            {
                this.Command.ReleaseConnection();
            }
        }

        /// <summary>
        /// Execute the query and returns the first column of the first row in the
        /// result set returned by the query. All other columns and rows are ignored.
        /// </summary>
        public object ExecuteScalar()
        {
            this.Command.AcquireConnection();

            try
            {
                return this.Command.ExecuteScalar();
            }
            finally
            {
                this.Command.ReleaseConnection();
            }
        }

        /// <summary>
        /// Execute a SQL statement against a connection object and return
        /// a System.Data.Common.DbDataReader using one of the System.Data.CommandBehavior
        /// values.
        /// </summary>
        /// <param name="behavior">An instance of System.Data.CommandBehavior.</param>
        public IDataReader ExecuteReader(CommandBehavior behavior)
        {
            this.Command.AcquireConnection();
            return this.Command.ExecuteReader(behavior);
        }

        /// <summary>
        /// Execute a SQL statement against a connection object and return
        /// a System.Data.Common.DbDataReader
        /// </summary>
        public IDataReader ExecuteReader()
        {
            this.Command.AcquireConnection();
            return this.Command.ExecuteReader();
        }

        /// <summary>
        /// Execute a SQL statement against a connection object and return
        /// a System.Collections.Generic.List with the returned values.
        /// </summary>
        /// <typeparam name="T">The System.Type of the elements in the System.Collections.Generic.List.</typeparam>
        public List<T> ExecuteReader<T>()
            where T : PlainEntity, new()
        {
            return this.Command.ReadList<T>((item, reader) => item.Fill(reader));
        }

        /// <summary>
        /// Execute a SQL statement against a connection object and return
        /// a collection of type System.Collections.Generic.List typed with the returned values
        /// mapped according the IResultSetMap instance
        /// </summary>
        /// <typeparam name="T">The System.Type of the elements in the System.Collections.Generic.List.</typeparam>
        /// <param name="mapAction">A delegate instance for custom mapping elements.</param>
        public List<T> ExecuteReader<T>(Action<T, IDataReader> mapAction)
            where T : PlainEntity, new()
        {
            return this.Command.ReadList<T>(mapAction);
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Release all resources used
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Release all resources used
        /// </summary>
        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.Command != null)
                {
                    if (this.Command.Transaction == null &&
                        this.Command.Connection != null)
                    {
                        this.Command.Connection.Close();
                        this.Command.Connection.Dispose();
                        this.Command.Connection = null;
                    }

                    this.Parameters.Clear();
                    this.Command = null;
                }
            }
        }

        #endregion
    }
}
